package wx

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

type Response interface {
	Err() ErrInfo
}

type ErrInfo struct {
	ErrCode    int    `json:"errcode"`
	ErrMessage string `json:"errmsg"`
}

func (e ErrInfo) Err() ErrInfo {
	return e
}

var client *http.Client

func init() {
	client = &http.Client{
		Transport:     &http.Transport{
			Proxy: func(request *http.Request) (u *url.URL, err error) {
				return url.Parse("http://127.0.0.1:1080/")
			},
		},
	}
}

func Get(link string, query map[string]string, dest Response) (err error) {
	u, err := url.Parse(link)
	if err != nil {
		return err
	}
	p := u.Query()
	for name, value := range query {
		p.Set(name, value)
	}
	u.RawQuery = p.Encode()
	r, err := client.Get(u.String())
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if bs, err := ioutil.ReadAll(r.Body); err != nil {
		return err
	} else if err := json.Unmarshal(bs, dest); err != nil {
		return err
	}
	switch dest.Err().ErrCode {
	case 0:
		return nil
	case 40164:
		return errors.Report2("服务器的IP地址不在白名单中", dest)
	case 40163, 40029:
		return errors.Called("授权码错误或过期", 212001)
	default:
		return errors.Report2(dest.Err().ErrMessage, dest)
	}
}

func Post(link string, query map[string]string, data interface{}, dest Response) (err error) {
	u, err := url.Parse(link)
	if err != nil {
		return err
	}
	p := u.Query()
	for name, value := range query {
		p.Set(name, value)
	}
	u.RawQuery = p.Encode()
	content, err := json.Marshal(data)
	if err != nil {
		return err
	}
	r, err := client.Post(u.String(), "application/json", bytes.NewReader(content))
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if bs, err := ioutil.ReadAll(r.Body); err != nil {
		return err
	} else if err := json.Unmarshal(bs, dest); err != nil {
		return err
	}
	switch dest.Err().ErrCode {
	case 0:
		return nil
	case 40164:
		return errors.Report2("服务器的IP地址不在白名单中", dest)
	case 40163, 40029:
		return errors.Called("授权码错误或过期", 212001)
	default:
		return errors.Report2(dest.Err().ErrMessage, dest)
	}
}

/**
 * 获取并缓存token
 */
type TokenInfo struct {
	AccessToken string `json:"access_token"` // "36_K6Q_NV8ZcjwhkGDR7ECrsTuxxOMjvzIa4aRq_T8rwTDoVzTROknfyiJtXmeKbY3tZk3OIVcMkUpCCe1Sy9xaSfLfwu94dwsvj6X5a--alivkEr-p_9_9EEM_xym6EkxnturLysswIA0Tn5QIJGSjADAYVH"
	ExpiresIn   int64  `json:"expires_in"`   // 7200
}

func FetchToken() (TokenInfo, error) {
	data := struct {
		ErrInfo
		TokenInfo
	}{}
	err := Get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential", map[string]string{
		"appid":  appid,
		"secret": secret,
	}, &data)
	return data.TokenInfo, err
}

var token string
var expires int64

func GetToken() string {
	now := time.Now().Unix()
	if token == "" || expires < now {
		data, err := FetchToken()
		if err != nil {
			panic(err)
		}
		token = data.AccessToken
		expires = now + data.ExpiresIn
	}
	return token
}

type UserInfo struct {
	Subscribe      int           `json:"subscribe"`       // 1
	OpenID         string        `json:"openid"`          // "o6HBW0Ym5nE4kQ--kQIwhbSoCXGs"
	NickName       string        `json:"nickname"`        // "阿银"
	Sex            int           `json:"sex"`             // 1
	Language       string        `json:"language"`        // zh_CN
	City           string        `json:"city"`            // ""
	Province       string        `json:"province"`        // ""
	Country        string        `json:"country"`         // ""
	HeadImgURL     string        `json:"headimgurl"`      // "http://thirdwx.qlogo.cn/mmopen/NqpgzFXbuHSuAgladv4xX1LW4P9pnOtLfsxvmBfhnVqq33f8BsoAJDULOatSpvFWJPQd8uicSxgnlotSZLCicjxdtJsPWLWxia3/132"
	SubscribeTime  int64         `json:"subscribe_time"`  // 1598597877
	Remark         string        `json:"remark"`          // ""
	Groupid        int64         `json:"groupid"`         // 0
	TagidList      []interface{} `json:"tagid_list"`      // []
	SubscribeScene string        `json:"subscribe_scene"` // "ADD_SCENE_SEARCH"
	QrScene        int           `json:"qr_scene"`        // 0
	QrSceneStr     string        `json:"qr_scene_str"`    // ""
}

// 后台拉取用户信息，只能拉取到已关注的用户的信息
func FetchUserInfo(openid string) (UserInfo, error) {
	data := struct {
		ErrInfo
		UserInfo
	}{}
	err := Get("https://api.weixin.qq.com/cgi-bin/user/info?lang=zh_CN", map[string]string{
		"access_token": GetToken(),
		"openid":       openid,
	}, &data)
	return data.UserInfo, err
}
